import {Component, Input, OnInit} from '@angular/core';
import {unByKey} from 'ol/Observable';
import Overlay from 'ol/Overlay';
import {getArea, getLength} from 'ol/sphere';
import {LineString, Polygon} from 'ol/geom';
import Draw from 'ol/interaction/Draw';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import {Vector as VectorSource} from 'ol/source';
import {Circle as CircleStyle, Fill, Stroke, Style} from 'ol/style';
import Map from 'ol/Map';
import {getTransform} from 'ol/proj';

@Component({
    selector: 'kylin-measure',
    templateUrl: './measure.component.html',
    styleUrls: ['./measure.component.less']
})
export class MeasureComponent {
    @Input() map: Map;

    genEle = new Array<HTMLElement>();
    source: any;
    vector: any;
    typeMenuVisible = false;
    /**
     * Currently drawn feature.
     * @type {module:ol/Feature~Feature}
     */
    sketch: any;

    /**
     * The help tooltip element.
     * @type {Element}
     */
    helpTooltipElement: any;

    /**
     * Overlay to show the help messages.
     * @type {module:ol/Overlay}
     */
    helpTooltip: any;

    /**
     * The measure tooltip element.
     * @type {Element}
     */
    measureTooltipElement: any;

    /**
     * Overlay to show the measurement.
     * @type {module:ol/Overlay}
     */
    measureTooltip;


    /**
     * Message to show when the user is drawing a polygon.
     * @type {string}
     */
    continuePolygonMsg = '单击此处继续绘制多边形';


    /**
     * Message to show when the user is drawing a line.
     * @type {string}
     */
    continueLineMsg = '单击继续绘制直线';

    typeSelect = 'length';
    state = false; // false mean 'off'
    draw: any; // global so we can remove it later
    viewListener: any;
    listeners = new Array<any>(); // record all listeners to removing later
    constructor() {
    }

    /**
     * Handle pointer move.
     * @param {module:ol/MapBrowserEvent~MapBrowserEvent} evt The event.
     */
    pointerMoveHandler = (evt) => {
        if (evt.dragging) {
            return;
        }
        /** @type {string} */
        let helpMsg = '点击开始绘图';
        if (this.sketch) {
            const geom = (this.sketch.getGeometry());
            if (geom instanceof Polygon) {
                helpMsg = this.continuePolygonMsg;
            } else if (geom instanceof LineString) {
                helpMsg = this.continueLineMsg;
            }
        }

        this.helpTooltipElement.innerHTML = helpMsg;
        this.helpTooltip.setPosition(evt.coordinate);

        this.helpTooltipElement.classList.remove('hidden');
    };
    /**
     * Format length output.
     * @param {module:ol/geom/LineString~LineString} line The line.
     * @return {string} The formatted length.
     */
    formatLength = (line) => {
        const lineCopy = line.clone();
        lineCopy.applyTransform(getTransform('EPSG:3857', 'EPSG:3857'));
        const length = getLength(lineCopy);
        // const length = getLength(line);
        let output;
        if (length > 100) {
            output = (Math.round(length / 1000 * 100) / 100) +
                ' ' + 'km';
        } else {
            output = (Math.round(length * 100) / 100) +
                ' ' + 'm';
        }
        return output;
    };


    /**
     * Format area output.
     * @param {module:ol/geom/Polygon~Polygon} polygon The polygon.
     * @return {string} Formatted area.
     */
    formatArea = (polygon) => {
        const polygonCopy = polygon.clone();
        polygonCopy.applyTransform(getTransform('EPSG:3857', 'EPSG:3857'));
        const area = getArea(polygonCopy);
        let output;
        if (area > 10000) {
            output = (Math.round(area / 1000000 * 100) / 100) +
                ' ' + 'km<sup>2</sup>';
        } else {
            output = (Math.round(area * 100) / 100) +
                ' ' + 'm<sup>2</sup>';
        }
        return output;
    };

    addInteraction = () => {
        const type = this.typeSelect === 'area' ? 'Polygon' : 'LineString';
        this.draw = new Draw({
            source: this.source,
            type: type as any,
            dragVertexDelay: 0,
            style: new Style({
                fill: new Fill({
                    color: 'rgba(255, 255, 255, 0.2)'
                }),
                stroke: new Stroke({
                    color: 'rgba(0, 0, 0, 0.5)',
                    lineDash: [10, 10],
                    width: 2
                }),
                image: new CircleStyle({
                    radius: 5,
                    stroke: new Stroke({
                        color: 'rgba(0, 0, 0, 0.7)'
                    }),
                    fill: new Fill({
                        color: 'rgba(255, 255, 255, 0.2)'
                    })
                })
            })
        });
        this.map.addInteraction(this.draw);

        this.createMeasureTooltip();
        this.createHelpTooltip();

        let listener;
        this.draw.on('drawstart', (evt) => {
            // set sketch
            this.sketch = evt.feature;

            /** @type {module:ol/coordinate~Coordinate|undefined} */
            let tooltipCoord = evt.coordinate;
            listener = this.sketch.getGeometry().on('change', (event) => {
                const geom = event.target;
                let output;
                if (geom instanceof Polygon) {
                    output = this.formatArea(geom);
                    tooltipCoord = geom.getInteriorPoint().getCoordinates();
                } else if (geom instanceof LineString) {
                    output = this.formatLength(geom);
                    tooltipCoord = geom.getLastCoordinate();
                }
                this.measureTooltipElement.innerHTML = output;
                this.measureTooltip.setPosition(tooltipCoord);
            });
        }, this);

        this.draw.on('drawend',
            () => {
                this.measureTooltipElement.className = 'util-measure-tooltip util-measure-tooltip-static';
                this.measureTooltip.setOffset([0, -7]);
                // unset sketch
                this.sketch = null;
                // unset tooltip so that a new one can be created
                this.measureTooltipElement = null;
                this.createMeasureTooltip();
                unByKey(listener);
            }, this);
    }


    /**
     * Creates a new help tooltip
     */
    createHelpTooltip = () => {
        if (this.helpTooltipElement) {
            this.helpTooltipElement.parentNode.removeChild(this.helpTooltipElement);
        }
        this.helpTooltipElement = document.createElement('div');
        this.genEle.push(this.helpTooltipElement);
        this.helpTooltipElement.className = 'util-measure-tooltip hidden';
        this.helpTooltip = new Overlay({
            element: this.helpTooltipElement,
            offset: [15, 0],
            positioning: 'center-left' as any
        });
        this.map.addOverlay(this.helpTooltip);
    }

    /**
     * Creates a new measure tooltip
     */
    createMeasureTooltip = () => {
        if (this.measureTooltipElement) {
            this.measureTooltipElement.parentNode.removeChild(this.measureTooltipElement);
        }
        this.measureTooltipElement = document.createElement('div');

        this.genEle.push(this.measureTooltipElement);

        this.measureTooltipElement.className = 'util-measure-tooltip util-measure-tooltip-measure';
        this.measureTooltip = new Overlay({
            element: this.measureTooltipElement,
            offset: [0, -15],
            positioning: 'bottom-center' as any
        });
        this.map.addOverlay(this.measureTooltip);
    }

    typeSelectChange(typeSelect: string) {
        this.typeSelect = typeSelect;
        this.typeMenuVisible = false;
        this.turnOn();
    }

    turnOn() {
        if (!this.isLastLayer()) {
            this.source = new VectorSource();
            this.vector = new VectorLayer({
                source: this.source,
                style: new Style({
                    fill: new Fill({
                        color: 'rgba(255, 255, 255, 0.2)'
                    }),
                    stroke: new Stroke({
                        color: '#ffcc33',
                        width: 2
                    }),
                    image: new CircleStyle({
                        radius: 7,
                        fill: new Fill({
                            color: '#ffcc33'
                        })
                    })
                })
            });
            this.map.addLayer(this.vector);
            this.viewListener = () => {
                this.helpTooltipElement.classList.add('hidden');
            };
            this.map.getViewport().addEventListener('mouseout', this.viewListener);
            this.listeners.push(this.map.on('pointermove', this.pointerMoveHandler));
        }
        if (this.draw) {
            this.map.removeInteraction(this.draw);
        }
        this.addInteraction();
        this.state = true;
    }

    turnOff() {
        this.map.removeOverlay(this.helpTooltip);
        this.map.removeInteraction(this.draw);
        this.map.removeOverlay(this.measureTooltip);
        this.map.removeLayer(this.vector);
        for (let i = 0; i < this.genEle.length; i++) {
            this.genEle[i].remove();
        }
    }

    isLastLayer() {
        return this.map.getLayers().item(this.map.getLayers().getLength() - 1) === this.vector;
    }

    cancel() {
        if (this.draw) {
            this.state = false;
            this.turnOff();
            this.measureTooltipElement = null;
            this.helpTooltipElement = null;
            this.map.getViewport().removeEventListener('mouseout', this.viewListener);
            unByKey(this.listeners);
        }
    }
}
